--[[
@FileName: Functions
@Date:     2018-04-11 04/11/18
@Author:   zhangpenghui
@Description:
]]

local function dump_value_(v)
	if type(v) == "string" then
		v = "\"" .. v .. "\""
	end
	return tostring(v)
end

function dump(value, description, nesting)
	if type(nesting) ~= "number" then nesting = 3 end

	local lookupTable = {}
	local result = {}

	local traceback = string.split(debug.traceback("", 2), "\n")
	Log.Info("dump from: " .. string.trim(traceback[3]))

	local function dump_(value, description, indent, nest, keylen)
		description = description or "<var>"
		local spc = ""
		if type(keylen) == "number" then
			spc = string.rep(" ", keylen - string.len(dump_value_(description)))
		end
		if type(value) ~= "table" then
			result[#result + 1 ] = string.format("%s%s%s = %s", indent, dump_value_(description), spc, dump_value_(value))
		elseif lookupTable[tostring(value)] then
			result[#result + 1 ] = string.format("%s%s%s = *REF*", indent, dump_value_(description), spc)
		else
			lookupTable[tostring(value)] = true
			if nest > nesting then
				result[#result + 1 ] = string.format("%s%s = *MAX NESTING*", indent, dump_value_(description))
			else
				result[#result + 1 ] = string.format("%s%s = {", indent, dump_value_(description))
				local indent2 = indent.."    "
				local keys = {}
				local keylen = 0
				local values = {}
				for k, v in pairs(value) do
					keys[#keys + 1] = k
					local vk = dump_value_(k)
					local vkl = string.len(vk)
					if vkl > keylen then keylen = vkl end
					values[k] = v
				end
				table.sort(keys, function(a, b)
					if type(a) == "number" and type(b) == "number" then
						return a < b
					else
						return tostring(a) < tostring(b)
					end
				end)
				for i, k in ipairs(keys) do
					dump_(values[k], k, indent2, nest + 1, keylen)
				end
				result[#result + 1] = string.format("%s}", indent)
			end
		end
	end
	dump_(value, description, "- ", 1)

	for i, line in ipairs(result) do
		print(line)
	end
end

function printf(fmt, ...)
	print(string.format(tostring(fmt), ...))
end

function checknumber(value, base)
	return tonumber(value, base) or 0
end

function checkint(value)
	return math.round(checknumber(value))
end

function checkbool(value)
	return(value ~= nil and value ~= false)
end

function checktable(value)
	if type(value) ~= "table" then value = {} end
	return value
end

function isset(hashtable, key)
	local t = type(hashtable)
	return(t == "table" or t == "userdata") and hashtable[key] ~= nil
end

local setmetatableindex_
setmetatableindex_ = function(t, index)
	if type(t) == "userdata" then
		local peer = tolua.getpeer(t)
		if not peer then
			peer = {}
			tolua.setpeer(t, peer)
		end
		setmetatableindex_(peer, index)
	else
		local mt = getmetatable(t)
		if not mt then mt = {} end
		if not mt.__index then
			mt.__index = index
			setmetatable(t, mt)
		elseif mt.__index ~= index then
			setmetatableindex_(mt, index)
		end
	end
end
setmetatableindex = setmetatableindex_

function clone(object)
	local lookup_table = {}
	local function _copy(object)
		if type(object) ~= "table" then
			return object
		elseif lookup_table[object] then
			return lookup_table[object]
		end
		local newObject = {}
		lookup_table[object] = newObject
		for key, value in pairs(object) do
			newObject[_copy(key)] = _copy(value)
		end
		return setmetatable(newObject, getmetatable(object))
	end
	return _copy(object)
end

function class(classname, ...)
	local cls = {__cname = classname}

	local supers = {...}
	for _, super in ipairs(supers) do
		local superType = type(super)
		assert(superType == "nil" or superType == "table" or superType == "function",
		string.format("class() - create class \"%s\" with invalid super class type \"%s\"",
		classname, superType))

		if superType == "function" then
			assert(cls.__create == nil,
			string.format("class() - create class \"%s\" with more than one creating function",
			classname));
            -- if super is function, set it to __create
			cls.__create = super
		elseif superType == "table" then
			if super[".isclass"] then
                -- super is native class
				assert(cls.__create == nil,
				string.format("class() - create class \"%s\" with more than one creating function or native class",
				classname));
				cls.__create = function() return super:create() end
			else
                -- super is pure lua class
				cls.__supers = cls.__supers or {}
				cls.__supers[#cls.__supers + 1] = super
				if not cls.super then
                    -- set first super pure lua class as class.super
					cls.super = super
				end
			end
		else
			error(string.format("class() - create class \"%s\" with invalid super type",
			classname), 0)
		end
	end

	cls.__index = cls
	if not cls.__supers or #cls.__supers == 1 then
		setmetatable(cls, {__index = cls.super})
	else
		setmetatable(cls, {__index = function(_, key)
			local supers = cls.__supers
			for i = 1, #supers do
				local super = supers[i]
				if super[key] then return super[key] end
			end
		end})
	end

	if not cls.Ctor then
        -- add default constructor
		cls.Ctor = function() end
	end
	cls.New = function(...)
		local instance
		if cls.__create then
			instance = cls.__create(...)
		else
			instance = {}
		end
		setmetatableindex(instance, cls)
		instance.class = cls
		instance:Ctor(...)
		return instance
	end
	cls.create = function(_, ...)
		return cls.New(...)
	end

	return cls
end

local iskindof_
iskindof_ = function(cls, name)
	local __index = rawget(cls, "__index")
	if type(__index) == "table" and rawget(__index, "__cname") == name then return true end

	if rawget(cls, "__cname") == name then return true end
	local __supers = rawget(__index, "__supers")
	if not __supers then return false end
	for _, super in ipairs(__supers) do
		if iskindof_(super, name) then return true end
	end
	return false
end

function iskindof(obj, classname)
	local t = type(obj)
	if t ~= "table" and t ~= "userdata" then return false end

	local mt
	if t == "userdata" then
		if tolua.iskindof(obj, classname) then return true end
		mt = tolua.getpeer(obj)
	else
		mt = getmetatable(obj)
	end
	if mt then
		return iskindof_(mt, classname)
	end
	return false
end

function import(moduleName, currentModuleName)
	local currentModuleNameParts
	local moduleFullName = moduleName
	local offset = 1

	while true do
		if string.byte(moduleName, offset) ~= 46 then -- .
			moduleFullName = string.sub(moduleName, offset)
			if currentModuleNameParts and #currentModuleNameParts > 0 then
				moduleFullName = table.concat(currentModuleNameParts, ".") .. "." .. moduleFullName
			end
			break
		end
		offset = offset + 1

		if not currentModuleNameParts then
			if not currentModuleName then
				local n, v = debug.getlocal(3, 1)
				currentModuleName = v
			end

			currentModuleNameParts = string.split(currentModuleName, ".")
		end
		table.remove(currentModuleNameParts, #currentModuleNameParts)
	end

	return require(moduleFullName)
end

function handler(obj, method)
	return function(...)
		return method(obj, ...)
	end
end

function math.newrandomseed()
	local ok, socket = pcall(function()
		return require("socket")
	end)

	if ok then
		math.randomseed(socket.gettime() * 1000)
	else
		math.randomseed(os.time())
	end
	math.random()
	math.random()
	math.random()
	math.random()
end

function math.round(value)
	value = checknumber(value)
	return math.floor(value + 0.5)
end

local pi_div_180 = math.pi / 180
function math.angle2radian(angle)
	return angle * pi_div_180
end

function math.radian2angle(radian)
	return radian * 180 / math.pi
end

function math.precise(num, len)
	return tonumber(string.format("%." .. len .. "f", num))
end

function math.clamp(v, _low, _high)
	if v < _low then
		v = _low
	elseif v > _high then
		v = _high
	end
	return v
end

function io.exists(path)
	local file = io.open(path, "r")
	if file then
		io.close(file)
		return true
	end
	return false
end

function io.readfile(path)
	local file = io.open(path, "r")
	if file then
		local content = file:read("*a")
		io.close(file)
		return content
	end
	return nil
end

function io.writefile(path, content, mode)
	mode = mode or "w+b"
	local file = io.open(path, mode)
	if file then
		if file:write(content) == nil then return false end
		io.close(file)
		return true
	else
		return false
	end
end

function io.pathinfo(path)
	local pos = string.len(path)
	local extpos = pos + 1
	while pos > 0 do
		local b = string.byte(path, pos)
		if b == 46 then -- 46 = char "."
			extpos = pos
		elseif b == 47 then -- 47 = char "/"
			break
		end
		pos = pos - 1
	end

	local dirname = string.sub(path, 1, pos)
	local filename = string.sub(path, pos + 1)
	extpos = extpos - pos
	local basename = string.sub(filename, 1, extpos - 1)
	local extname = string.sub(filename, extpos)
	return {
	dirname = dirname,
	filename = filename,
	basename = basename,
	extname = extname
	}
end

function io.filesize(path)
	local size = false
	local file = io.open(path, "r")
	if file then
		local current = file:seek()
		size = file:seek("end")
		file:seek("set", current)
		io.close(file)
	end
	return size
end

function table.nums(t)
	local count = 0
	for k, v in pairs(t) do
		count = count + 1
	end
	return count
end

function table.keys(hashtable)
	local keys = {}
	for k, v in pairs(hashtable) do
		keys[#keys + 1] = k
	end
	return keys
end

function table.values(hashtable)
	local values = {}
	for k, v in pairs(hashtable) do
		values[#values + 1] = v
	end
	return values
end

function table.merge(dest, src)
	for k, v in pairs(src) do
		dest[k] = v
	end
end

function table.insertto(dest, src, begin)
	begin = checkint(begin)
	if begin <= 0 then
		begin = #dest + 1
	end

	local len = #src
	for i = 0, len - 1 do
		dest[i + begin] = src[i + 1]
	end
end

function table.indexof(array, value, begin)
	for i = begin or 1, #array do
		if array[i] == value then return i end
	end
	return false
end

function table.keyof(hashtable, value)
	for k, v in pairs(hashtable) do
		if v == value then return k end
	end
	return nil
end

function table.removebyvalue(array, value, removeall)
	local c, i, max = 0, 1, #array
	while i <= max do
		if array[i] == value then
			table.remove(array, i)
			c = c + 1
			i = i - 1
			max = max - 1
			if not removeall then break end
		end
		i = i + 1
	end
	return c
end

function table.map(t, fn)
	for k, v in pairs(t) do
		t[k] = fn(v, k)
	end
end

function table.walk(t, fn)
	for k, v in pairs(t) do
		fn(v, k)
	end
end

function table.filter(t, fn)
	for k, v in pairs(t) do
		if not fn(v, k) then t[k] = nil end
	end
end

function table.unique(t, bArray)
	local check = {}
	local n = {}
	local idx = 1
	for k, v in pairs(t) do
		if not check[v] then
			if bArray then
				n[idx] = v
				idx = idx + 1
			else
				n[k] = v
			end
			check[v] = true
		end
	end
	return n
end

function table.insertbykey( t, v, key )
	if #t == 0 then
		table.insert(t, v)
	else
		for i, val in ipairs(t) do
			if val[key] <= v[key] then
				table.insert(t, i, v)
				break
			elseif i == #t then
				table.insert(t, v)
			end
		end
	end
end

string._htmlspecialchars_set = {}
string._htmlspecialchars_set["&"] = "&amp;"
string._htmlspecialchars_set["\""] = "&quot;"
string._htmlspecialchars_set["'"] = "&#039;"
string._htmlspecialchars_set["<"] = "&lt;"
string._htmlspecialchars_set[">"] = "&gt;"

function string.htmlspecialchars(input)
	for k, v in pairs(string._htmlspecialchars_set) do
		input = string.gsub(input, k, v)
	end
	return input
end

function string.restorehtmlspecialchars(input)
	for k, v in pairs(string._htmlspecialchars_set) do
		input = string.gsub(input, v, k)
	end
	return input
end

function string.nl2br(input)
	return string.gsub(input, "\n", "<br />")
end

function string.text2html(input)
	input = string.gsub(input, "\t", "    ")
	input = string.htmlspecialchars(input)
	input = string.gsub(input, " ", "&nbsp;")
	input = string.nl2br(input)
	return input
end

function string.split(input, delimiter)
	input = tostring(input)
	delimiter = tostring(delimiter)
	if(delimiter=='') then return false end
	local pos, arr = 0, {}
    -- for each divider found
	for st, sp in function() return string.find(input, delimiter, pos, true) end do
		table.insert(arr, string.sub(input, pos, st - 1))
		pos = sp + 1
	end
	table.insert(arr, string.sub(input, pos))
	return arr
end

function string.ltrim(input)
	return string.gsub(input, "^[ \t\n\r]+", "")
end

function string.rtrim(input)
	return string.gsub(input, "[ \t\n\r]+$", "")
end

function string.trim(input)
	input = string.gsub(input, "^[ \t\n\r]+", "")
	return string.gsub(input, "[ \t\n\r]+$", "")
end

function string.ucfirst(input)
	return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2)
end

local function urlencodechar(char)
	return "%" .. string.format("%02X", string.byte(char))
end
function string.urlencode(input)
    -- convert line endings
	input = string.gsub(tostring(input), "\n", "\r\n")
    -- escape all characters but alphanumeric, '.' and '-'
	input = string.gsub(input, "([^%w%.%- ])", urlencodechar)
    -- convert spaces to "+" symbols
	return string.gsub(input, " ", "+")
end

function string.urldecode(input)
	input = string.gsub(input, "+", " ")
	input = string.gsub(input, "%%(%x%x)", function(h) return string.char(checknumber(h, 16)) end)
	input = string.gsub(input, "\r\n", "\n")
	return input
end

function string.utf8len(input)
	local len = string.len(input)
	local left = len
	local cnt = 0
	local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}
	while left ~= 0 do
		local tmp = string.byte(input, -left)
		local i = #arr
		while arr[i] do
			if tmp >= arr[i] then
				left = left - i
				break
			end
			i = i - 1
		end
		cnt = cnt + 1
	end
	return cnt
end

function string.formatnumberthousands(num)
	local formatted = tostring(checknumber(num))
	local k
	while true do
		formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
		if k == 0 then break end
	end
	return formatted
end

function string.isNullOrEmpty(string)
	return string == nil or string == ""
end

function printTable(t)
    function printTableRecursive(t, record, depth)
        record[t] = true
    
        local tabs = ""
        for i = 1,depth do 
            tabs = tabs.."\t" 
        end    
        local tabsChild = tabs.."\t"
    
        if depth == 0 then
            Log.Info(tabs..tostring(t).." {")
        end
    
        for k,v in pairs(t) do
            local element = tabsChild.."k=("..type(k)..")"..tostring(k)..", v=("..type(v)..")"..tostring(v)
            if type(v) == "table" then
                if record[v] then
                    Log.Info(element..": skip exist...")
                else
                    Log.Info(element.." {")                
                    printTableRecursive(v, record, depth + 1)
                end
            else
                Log.Info(element)
            end
        end    
        Log.Info(tabs.."}")
    end

    record = {}
    printTableRecursive(t, record, 0)
end

function CSArrayToTable(arg)
	t = {}
	for i=0, arg.Length-1 do
		table.insert(t, arg[i])
	end
	return t
end

function getIntPart(x)
	if x <= 0 then
	   return math.ceil(x)
	end
	if math.ceil(x) == x then
	   x = math.ceil(x)
	else
	   x = math.ceil(x) - 1
	end
	return x
end

